package com.tbit.uqbike.client.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.tbit.common.config.TbitProperties;
import com.tbit.common.utils.IpUtil;
import com.tbit.common.utils.TbitHttpServiceUtil;
import com.tbit.uqbike.client.constant.BrandConstant;
import com.tbit.uqbike.client.constant.ResponseCode;
import com.tbit.uqbike.client.constant.SystemConstant;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.remote.sms.AliSms;
import com.tbit.uqbike.client.remote.sms.BSCXSmsApi;
import com.tbit.uqbike.client.remote.sms.ClzySmsAuthUtil;
import com.tbit.uqbike.client.remote.sms.InternationalSmsUtil;
import com.tbit.uqbike.client.remote.sms.apistore.ApiStoreMsgUtils;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.Assert;
import com.tbit.uqbike.client.util.DateTimeUtil;
import com.tbit.uqbike.client.util.OutputUtil;
import com.tbit.uqbike.client.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static com.tbit.uqbike.client.constant.RedisConstant.*;

/**
 * 短信
 *
 * @author Leon
 * 2017年2月24日 下午5:51:05
 */
@Controller
@RequestMapping("/sms")
@RefreshScope
public class SmsController {
    private final static org.apache.logging.log4j.Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    @Autowired
    private SmsLogService smsLogService;
    @Autowired
    private SmsAuthConfigService smsAuthConfigService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private CountryService countryService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private TbitProperties tbitProperties;
    @Autowired
    private CaptchaService captchaService;

    @Value("${tbit-system.sendAuthCodeCountByDay:10000}")
    private Integer sendAuthCodeCountByDay;

    @Value("${tbit-system.numberSegment}")
    private String numberSegment;

    /**
     * 发送验证码
     *
     * @param accountId   品牌id
     * @param countryCode 国家代码
     * @param phone       手机号码
     * @param flag        发送六位验证码的标识
     * @ignoreParams request
     */
    @ResponseBody
    @RequestMapping("/sendAuthCode")
    public Result<Void> sendAuthCode(Integer accountId, String countryCode, String phone, HttpServletRequest request, String flag) {
        /* 校验 */
        Assert.notNull(accountId, "-10001", "账户id不能为空");
        Assert.hasLength(phone, "-20001", "手机号码不能为空");
        String authCode = StringUtil.getAuthCode(flag == null ? SystemConstant.AUTH_CODE_LENGTH : 6);
        if (StringUtils.isBlank(countryCode)) {
            countryCode = "86";
        }
        if (countryCode.contains("+")) {
            countryCode = countryCode.replace("+", "");
        }

        Country country = countryService.getById(countryCode);
        if (country == null) {
            throw new BaseException("-20006", "国家码不存在");
        }
        /*手机号码格式匹配*/
        String pattern = country.getCountryPhoneRegex();
        boolean isMatch = Pattern.matches(pattern, phone);
        if (!isMatch) {
            throw new BaseException("-20007", "手机号码格式错误");
        }

        /*拦截虚拟号段手机号发送验证码*/
        if (!checkValidPhone(accountId, phone)){
            throw new BaseException("当前手机号不可注册，请换号码或咨询客服。");
        }

        /* 存入redis中，有效时间按照配置来 */
        redisService.add(phone, authCode, SystemConstant.AUTH_CODE_VALIDITY);


        /*登录日志*/
        String ip = IpUtil.getClientIp(request);
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            userAgent = "";
        }
        if (userAgent.toLowerCase().contains("window")) {
            throw new BaseException("-3036", "被加入黑名单，功能被限制");
        }
        /*来自微信的请求一般会带上micromessenger,支付宝小程序会带alipay，app附带gxcx-app 没有拦截*/
        if (!userAgent.toLowerCase().contains("micromessenger") && !userAgent.toLowerCase().contains("gxcx-app") && !userAgent.toLowerCase().contains("alipay")) {
            throw new BaseException("-3036", "被加入黑名单，功能被限制");
        }
        String tmp = redisService.get(ip);
        if (tmp != null) {
            logger.info("{}在ip黑名单中，手机号: {}", ip, phone);
            throw new BaseException("-3041", "操作过于频繁，请稍等一会");
        } else {
            if (redisService.incr("incr." + ip, 1800) > 10) {
                redisService.add(ip, ip, 3600);
            }
        }
        flowControl(request, phone, accountId);
        /*限制每个手机号码每天只能发送3条短信*/
        String redisPhone = redisService.get("phone=" + phone);
        long secondsUntilMidnight = DateTimeUtil.getSecondsUntilMidnight();
        if (redisPhone != null && Integer.parseInt(redisPhone) > 3) {
            throw new BaseException("-3041", "验证码达上限，请明日再试，或使用快捷登录。");
        } else {
            if (redisPhone == null) {
                redisPhone = "0";
            }
            redisPhone = String.valueOf(Integer.parseInt(redisPhone) + 1);
            redisService.add("phone=" + phone, redisPhone, secondsUntilMidnight);
        }
        /*限制一个品牌一天只能发送10000条短信*/
        if (redisService.incr("incr." + accountId, secondsUntilMidnight) > sendAuthCodeCountByDay) {
            throw new BaseException("-3041", "验证码达上限，请明日再试，或使用快捷登录。");
        }
        if (countryCode.equals("86")) {
            BrandRemoteConfig brandRemoteConfigDB = cacheService.getBrandRemoteConfig(accountId,
                    BrandConstant.TBIT_CONFIG);
            //优先使用自研短信
            String templateId = cacheService.getBrandRemoteConfigValue(accountId,
                    BrandConstant.SMS_TEMPLATEID);
            boolean send = false;
            if (templateId != null && brandRemoteConfigDB != null) {
                String key = brandRemoteConfigDB.getParamKV();
                String[] strings = key.split("&");
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("code", authCode);
                boolean result = TbitHttpServiceUtil.sendSms(strings[0].substring(strings[0].indexOf("=") + 1),
                        strings[1].substring(strings[1].indexOf("=") + 1)
                        , strings[2].substring(strings[2].indexOf("=") + 1), phone, jsonObject.toJSONString(), Integer.valueOf(templateId));
                if (result) {
                    send = true;
                }
            }
            //发送失败转旧版发送
            if (!send) {
                sendSms(accountId, phone, authCode);
            }
        } else {
            /*国际短信兼容*/
            phone = "00" + countryCode + phone;
            String content = "【Youqudrive Travel】 Your verification code is " + authCode + ". If you are not operating by yourself, please ignore this message";
            InternationalSmsUtil.send(phone, content);
        }
        /*手机号码限制号段*/
        SmsLog smsLog = new SmsLog();
        smsLog.setAccountId(accountId);
        smsLog.setContent("模版短信，验证码为=" + authCode);
        smsLog.setPhone(phone);
        smsLog.setSendTime(DateTimeUtil.getNowTime());
        smsLogService.insert(smsLog);
        return Result.success();
    }

    /**
     * 根据userAgent进行流量控制
     * @param request 请求
     * @param phone 手机号
     * @param accountId 品牌id
     */
    void flowControl(HttpServletRequest request, String phone, Integer accountId) {
        String userAgent = request.getHeader("User-Agent");
        if (StringUtils.isNotBlank(userAgent)) {
            if (redisService.get(String.format(SEND_CODE_USER_AGENT, accountId, userAgent)) != null) {
                logger.info("短信验证码发送被限制,品牌id:{},ip:{},userAgent:{},phone={}", accountId, IpUtil.getClientIp(request), userAgent, phone);
                throw new BaseException("-3041", "操作过于频繁，请稍等一会");
            }
            if (redisService.incr(String.format(INCR_USER_AGENT, accountId, userAgent), 10) > 5) {
                //10秒内有五次，直接限制一小时
                redisService.add(String.format(SEND_CODE_USER_AGENT, accountId, userAgent), IpUtil.getClientIp(request), 3600);
            }
        }
    }

    /**
     * 发送验证码-带人机校验
     *
     * @param accountId   品牌id
     * @param countryCode 国家代码
     * @param phone       手机号码
     * @param flag        发送六位验证码的标识
     * @param captcha     人机校验结果，每天第一次发送可不传
     * @ignoreParams request
     */
    @ResponseBody
    @PostMapping("/v2/sendAuthCode")
    public Result<Void> sendAuthCodeV2(Integer accountId, String countryCode, String phone, HttpServletRequest request, String flag, String captcha) {
        // 检查是否无需人机校验
        if (captcha == null && redisService.get(CAPTCHA_PREFIX + phone) != null) {
            throw new BaseException("未进行人机校验，请重试");
        }

        // 验证人机校验
        if (captcha != null) {
            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaVerification(captcha);
            ResponseModel verifyResult = captchaService.verification(captchaVO);
            if (!verifyResult.isSuccess()) {
                switch (verifyResult.getRepCode()) {
                    case "6110":
                        throw new BaseException("验证码已失效，请重试");
                    case "9999":
                        throw new BaseException("人机校验时发生错误，请重试");
                    case "0011": // 参数不能为空
                    case "6111": // 验证码已失效，请重新获取
                    default:
                        throw new BaseException("人机校验不通过，请重试");
                }
            }
        }
        Result<Void> result = sendAuthCode(accountId, countryCode, phone, request, flag);

        // 设置今日手机号人机校验缓存
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime nextDay = now.plusDays(1).with(LocalTime.MIN);
        Duration timeToNextDay = Duration.between(now, nextDay);
        redisService.add(CAPTCHA_PREFIX + phone, "1", timeToNextDay.getSeconds());

        return result;
    }

    /**
     * 检查是否需要进行人机校验
     *
     * @param phone 要发送验证码的手机号
     */
    @ResponseBody
    @PostMapping("/needCaptcha")
    public Result<Boolean> canSendAuthCode(String phone) {
        return Result.success(redisService.get(CAPTCHA_PREFIX + phone) != null);
    }

    /**
     * 旧版发送短信
     *
     * @param accountId 品牌id
     * @param phone 手机号码
     * @param authCode 验证码
     */
    private void sendSms(Integer accountId,String phone,String authCode){
        //兼容波思出行账号使用特定短信api发送,暂时不考虑后期兼容
        if (accountId.equals(100019)) {
            BSCXSmsApi.sendSms(phone, authCode);
        } else {
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(accountId,
                    BrandConstant.CLYZ_SMS);
            if (brandRemoteConfig != null && brandRemoteConfig.getParamKV().contains("account=") && brandRemoteConfig.getParamKV().contains("&password=")) {
                /*存储格式为account=xxx&password=xxx*/
                if (brandRemoteConfig.getParamKV().contains("account=") && brandRemoteConfig.getParamKV().contains("&password=")) {
                    String key = brandRemoteConfig.getParamKV();
                    String[] strings = key.split("&");
                    String account = strings[0].substring(strings[0].indexOf("=") + 1);
                    String password = strings[1].substring(strings[1].indexOf("=") + 1);
                    //您的验证码是#code#。如非本人操作，请忽略本短信
                    Account accountDB = cacheService.getAccount(accountId);
                    String content = "【" + accountDB.getName() + "】您的验证码是" + authCode + "。如非本人操作，请忽略本短信";
                    Boolean result = ClzySmsAuthUtil.sendSms(account, password, content, phone);
                    if (!result) {
                        throw new BaseException("-101", "系统异常");
                    }
                } else {
                    throw new BaseException("-103", "系统配置错误");
                }
            } else {
                /* 兼容短信验证码数据库配置，存在即是使用数据库配置，暂时只支持寻程发送 */
                SmsAuthConfig smsAuthConfig = smsAuthConfigService.getByAccountId(accountId);
                if (smsAuthConfig != null) {
                    /*from为0代表寻程，1代表阿里云*/
                    if (smsAuthConfig.getFrom() == 0) {
                        ApiStoreMsgUtils.send(phone, authCode, smsAuthConfig.getSmsCodeKey(), Integer.valueOf(smsAuthConfig.getSmsModelId()));
                    } else if (smsAuthConfig.getFrom() == 1) {
                        /*兼容阿里云存放秘钥，用分号分割*/
                        String[] strings = smsAuthConfig.getSmsCodeKey().split(";");
                        AliSms.sendSms(strings[0], strings[1], smsAuthConfig.getRemark(), smsAuthConfig.getSmsModelId(), phone, authCode);
                    }
                }
            }
        }
    }

    /**
     * 添加黑名单，测试使用
     *
     * @param ip       知识产权
     * @param phone    电话
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     * @deprecated
     */
    @RequestMapping("/add")
    public void add(String ip, String phone, HttpSession session, HttpServletRequest request,
                    HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            String tmp = redisService.get(ip);
            if (tmp == null) {
                redisService.add(ip, ip);
            } else {
                throw new BaseException("-3060", "已加入黑名单");
            }
			/*if(phone!=null){
				String phones = phone;
				String hPhone= redisService.get("hmd");
				if(hPhone!=null){
					phones = hPhone+","+phone;
				}
				redisService.add("hmd",phones);
			}*/

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 校验手机号是否有效手机号
     * @param accountId 品牌id
     * @param phone 手机号
     * @return boolean
     */
    private Boolean checkValidPhone(Integer accountId, String phone) {

        String configValue = cacheService.getBrandRemoteConfigValue(accountId, BrandConstant.BLOCK_VIRTUAL_PHONE_SWITCH);
        if (StrUtil.isEmpty(configValue) || "0".equals(configValue)) {
            return true;
        }

        if (StrUtil.isEmpty(numberSegment)) {
            logger.info("未配置号段-{}", accountId);
            return true;
        }

        List<String> list = Arrays.asList(numberSegment.split(","));
        String substring = phone.substring(0, 3);

        return list.stream().noneMatch(s -> s.equals(substring));
    }


}